package com.liuyjy.es.wrapper;

import com.google.common.reflect.TypeToken;
import com.liuyjy.core.exception.EsException;
import com.liuyjy.es.annotation.EsDocument;
import com.liuyjy.utils.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import java.util.*;
import java.util.function.Consumer;

/**
 * @Author liuyjy
 * @Date 2025/2/26 11:34
 * @Description: 基础包装器类，用于构建Elasticsearch查询和更新请求。
 */
@Slf4j
public abstract class EsBaseWrapper<T, R, Children extends EsBaseWrapper<T, R, Children>> {

    /**
     * 构造器
     */
    protected final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    /**
     * 排序
     */
    protected final List<SortBuilder<?>> sorts = new ArrayList<>();
    /**
     * 聚合
     */
    protected final List<AggregationBuilder> aggregations = new ArrayList<>();
    /**
     * 更新字段
     */
    protected final Map<String, Object> updateMap = new HashMap<>();
    /**
     * 分页 开始值
     */
    public Long current;
    /**
     * 分页 结束值
     */
    public Long size;
    /**
     * 搜索字段
     */
    protected String[] includes;
    /**
     * 排除字段
     */
    protected String[] excludes;
    /**
     * 索引名称
     */
    protected Set<String> indexNameList = new HashSet<>();

    private T entity;


    /**
     * 实体类型(主要用于确定泛型以及取TableInfo缓存)
     */
    private Class<T> entityClass;

    public T getEntity() {
        return entity;
    }

    public Children setEntity(T entity) {
        this.entity = entity;
        return self();
    }

    public Class<T> getEntityClass() {
        if (entityClass == null && entity != null) {
            entityClass = (Class<T>) entity.getClass();
        }
        return entityClass;
    }

    public Children setEntityClass(Class<T> entityClass) {
        if (entityClass != null) {
            this.entityClass = entityClass;
        }
        return self();
    }



    /**
     * 获取当前实例
     *
     * @return 当前实例
     */
    protected abstract Children self();

    // ========== 逻辑组合 ==========

    /**
     * 逻辑与
     *
     * @param consumer 条件包装器
     * @return 当前实例
     */
    protected abstract Children and(Consumer<Children> consumer);

    /**
     * 逻辑或
     *
     * @param consumer 条件包装器
     * @return 当前实例
     */
    protected abstract Children or(Consumer<Children> consumer);

    // 获取当前实例
//    protected Children self() {
//        return (Children) this;
//    }

    /**
     * 字段转换为字符串
     *
     * @param column 字段
     * @return 字段字符串
     */
    protected String columnToString(R column) {
        return (String) column;
    }

    /**
     * 字段转换为字符串组
     *
     * @param columns 字段
     * @return 字符串数组
     */
    protected String[] columnsToString(R... columns) {
        return Arrays.stream(columns).map(this::columnToString).toArray(String[]::new);
    }

    /**
     * 设置索引名称
     *
     * @param indexNames 索引名称
     * @return 当前实例
     */
    protected Children setIndexNames(String... indexNames) {
        if (EmptyUtil.isEmpty(indexNames)) {
            return self();
        }
        this.indexNameList = new HashSet<>(Arrays.asList(indexNames));
        return self();
    }

    /**
     * 等值查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    protected Children eq(R field, Object value) {
        boolQuery.must(QueryBuilders.termQuery(columnToString(field), value));
        return self();
    }

    /**
     * 条件等值查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    protected Children eq(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return eq(field, value);
    }

    /**
     * 不等值查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    public Children ne(R field, Object value) {
        boolQuery.mustNot(QueryBuilders.termQuery(columnToString(field), value));
        return self();
    }

    /**
     * 条件不等值查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    public Children ne(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return ne(field, value);
    }

    /**
     * 大于查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    public Children gt(R field, Object value) {
        boolQuery.must(QueryBuilders.rangeQuery(columnToString(field)).gt(value));
        return self();
    }

    /**
     * 条件大于查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    public Children gt(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return gt(field, value);
    }


    /**
     * 小于查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    public Children lt(R field, Object value) {
        boolQuery.must(QueryBuilders.rangeQuery(columnToString(field)).lt(value));
        return self();
    }

    /**
     * 条件小于查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    public Children lt(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return lt(field, value);
    }

    /**
     * 大于等于查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    public Children gte(R field, Object value) {
        boolQuery.must(QueryBuilders.rangeQuery(columnToString(field)).gte(value));
        return self();
    }


    /**
     * 条件大于等于查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    public Children gte(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return gte(field, value);
    }

    /**
     * 小于等于查询
     *
     * @param field 字段
     * @param value 值
     * @return 当前实例
     */
    public Children lte(R field, Object value) {
        boolQuery.must(QueryBuilders.rangeQuery(columnToString(field)).lte(value));
        return self();
    }

    /**
     * 条件小于等于查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return 当前实例
     */
    public Children lte(boolean condition, R field, Object value) {
        if (!condition) {
            return self();
        }
        return lte(field, value);
    }

    /**
     * 地理空间查询（距离范围），升序排序
     *
     * @param field    字段
     * @param geoPoint 地理点
     * @param distance 距离
     * @return 当前实例
     */
    public Children geoWithinAsc(R field, GeoPoint geoPoint, String distance) {
        return geoWithin(field, geoPoint, distance, SortOrder.ASC);
    }

    /**
     * 条件地理空间查询（距离范围），升序排序
     *
     * @param condition 条件
     * @param field     字段
     * @param geoPoint  地理点
     * @param distance  距离
     * @return 当前实例
     */
    public Children geoWithinAsc(boolean condition, R field, GeoPoint geoPoint, String distance) {
        return geoWithin(condition, field, geoPoint, distance, SortOrder.ASC);
    }

    /**
     * 地理空间查询（距离范围），降序排序
     *
     * @param field    字段
     * @param geoPoint 地理点
     * @param distance 距离
     * @return 当前实例
     */
    public Children geoWithinDesc(R field, GeoPoint geoPoint, String distance) {
        return geoWithin(field, geoPoint, distance, SortOrder.DESC);
    }


    /**
     * 条件地理空间查询（距离范围），降序排序
     *
     * @param condition 条件
     * @param field     字段
     * @param geoPoint  地理点
     * @param distance  距离
     * @return 当前实例
     */
    public Children geoWithinDesc(boolean condition, R field, GeoPoint geoPoint, String distance) {
        return geoWithin(condition, field, geoPoint, distance, SortOrder.DESC);
    }


    /**
     * 地理空间查询（距离范围）
     *
     * @param field    字段
     * @param geoPoint 地理点
     * @param distance 距离
     * @return 当前实例
     */
    public Children geoWithin(R field, GeoPoint geoPoint, String distance) {
        return geoWithin(field, geoPoint, distance, null);
    }

    /**
     * 地理空间查询（距离范围）
     *
     * @param field     字段
     * @param geoPoint  地理点
     * @param distance  距离
     * @param sortOrder 排序方式
     * @return 当前实例
     */
    public Children geoWithin(R field, GeoPoint geoPoint, String distance, SortOrder sortOrder) {
        GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery(columnToString(field))
                .point(geoPoint)
                .distance(distance);

        boolQuery.filter(geoQuery);
        if (sortOrder != null) {
            sorts.add(SortBuilders.geoDistanceSort(columnToString(field), geoPoint.lat(), geoPoint.lon())
                    .order(sortOrder));
        }
        return self();
    }


    /**
     * 条件地理空间查询（距离范围）
     *
     * @param condition 条件
     * @param field     字段
     * @param geoPoint  地理点
     * @param distance  距离
     * @param sortOrder 排序方式
     * @return 当前实例
     */
    public Children geoWithin(boolean condition, R field, GeoPoint geoPoint, String distance, SortOrder sortOrder) {
        if (!condition) {
            return self();
        }
        return geoWithin(field, geoPoint, distance, sortOrder);
    }

    /**
     * 条件地理空间查询（距离范围）
     *
     * @param condition 条件
     * @param field     字段
     * @param geoPoint  地理点
     * @param distance  距离
     * @return 当前实例
     */
    public Children geoWithin(boolean condition, R field, GeoPoint geoPoint, String distance) {
        if (!condition) {
            return self();
        }
        return geoWithin(field, geoPoint, distance);
    }

    /**
     * IN查询
     *
     * @param field  字段
     * @param values 值集合
     * @return 当前实例
     */
    public Children in(R field, Collection<?> values) {
        boolQuery.must(QueryBuilders.termsQuery(columnToString(field), values));
        return self();
    }

    /**
     * 条件IN查询
     *
     * @param condition 条件
     * @param field     字段
     * @param values    值集合
     * @return 当前实例
     */
    public Children in(boolean condition, R field, Collection<?> values) {
        if (!condition) {
            return self();
        }
        return in(field, values);
    }

    /**
     * NOT IN查询
     *
     * @param field  字段
     * @param values 值集合
     * @return 当前实例
     */
    public Children notIn(R field, Collection<?> values) {
        boolQuery.mustNot(QueryBuilders.termsQuery(columnToString(field), values));
        return self();
    }

    /**
     * 条件NOT IN查询
     *
     * @param condition 条件
     * @param field     字段
     * @param values    值集合
     * @return 当前实例
     */
    public Children notIn(boolean condition, R field, Collection<?> values) {
        if (!condition) {
            return self();
        }
        return notIn(field, values);
    }

    /**
     * BETWEEN查询
     *
     * @param field 字段
     * @param from  开始
     * @param to    结束
     * @return Children
     */
    public Children between(R field, Object from, Object to) {
        boolQuery.must(QueryBuilders.rangeQuery(columnToString(field)).gte(from).lte(to));
        return self();
    }

    /**
     * 条件BETWEEN查询
     *
     * @param condition 条件
     * @param field     字段
     * @param from      开始
     * @param to        结束
     * @return Children
     */
    public Children between(boolean condition, R field, Object from, Object to) {
        if (!condition) {
            return self();
        }
        return between(field, from, to);
    }


    /**
     * NOT BETWEEN查询
     *
     * @param field 字段
     * @param from  开始
     * @param to    结束
     * @return Children
     */
    public Children notBetween(R field, Object from, Object to) {
        boolQuery.mustNot(QueryBuilders.rangeQuery(columnToString(field)).gte(from).lte(to));
        return self();
    }

    /**
     * 条件NOT BETWEEN查询
     *
     * @param condition 条件
     * @param field     字段
     * @param from      开始
     * @param to        结束
     * @return Children
     */
    public Children notBetween(boolean condition, R field, Object from, Object to) {
        if (!condition) {
            return self();
        }
        return notBetween(field, from, to);
    }


    /**
     * 模糊查询
     *
     * @param field 字段
     * @param value 值
     * @return Children
     */
    public Children like(R field, String value) {
        boolQuery.must(QueryBuilders.wildcardQuery(columnToString(field), "*" + value + "*"));
        return self();
    }

    /**
     * 条件模糊查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return Children
     */
    public Children like(boolean condition, R field, String value) {
        if (!condition) {
            return self();
        }
        return like(field, value);
    }

    /**
     * NOT 模糊查询
     *
     * @param field 字段
     * @param value 值
     * @return Children
     */
    public Children notLike(R field, String value) {
        boolQuery.mustNot(QueryBuilders.wildcardQuery(columnToString(field), "*" + value + "*"));
        return self();
    }

    /**
     * 条件NOT 模糊查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return Children
     */
    public Children notLike(boolean condition, R field, String value) {
        if (!condition) {
            return self();
        }
        return notLike(field, value);
    }

    /**
     * 左模糊查询
     *
     * @param field 字段
     * @param value 值
     * @return Children
     */
    public Children likeLeft(R field, String value) {
        boolQuery.must(QueryBuilders.wildcardQuery(columnToString(field), value + "*"));
        return self();
    }

    /**
     * 条件左模糊查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return Children
     */
    public Children likeLeft(boolean condition, R field, String value) {
        if (!condition) {
            return self();
        }
        return likeLeft(field, value);
    }

    /**
     * 右模糊查询
     *
     * @param field 字段
     * @param value 值
     * @return Children
     */
    public Children likeRight(R field, String value) {
        boolQuery.must(QueryBuilders.wildcardQuery(columnToString(field), "*" + value));
        return self();
    }

    /**
     * 条件右模糊查询
     *
     * @param condition 条件
     * @param field     字段
     * @param value     值
     * @return Children
     */
    public Children likeRight(boolean condition, R field, String value) {
        if (!condition) {
            return self();
        }
        return likeRight(field, value);
    }

    /**
     * IS NOT NULL查询
     *
     * @param field 字段
     * @return Children
     */
    public Children isNotNull(R field) {
        boolQuery.mustNot(QueryBuilders.existsQuery(columnToString(field)));
        return self();
    }

    /**
     * IS NOT NULL查询
     *
     * @param condition 条件
     * @param field     字段
     * @return Children
     */
    public Children isNotNull(boolean condition, R field) {
        if (!condition) {
            return self();
        }
        return isNotNull(field);
    }

    /**
     * IS NULL查询
     *
     * @param field 字段
     * @return Children
     */
    public Children isNull(R field) {
        boolQuery.must(QueryBuilders.existsQuery(columnToString(field)));
        return self();
    }

    /**
     * IS NULL查询
     *
     * @param condition 条件
     * @param field     字段
     * @return Children
     */
    public Children isNull(boolean condition, R field) {
        if (!condition) {
            return self();
        }
        return isNull(field);
    }


    /**
     * 升序
     *
     * @param field 字段
     * @return Children
     */
    public Children orderByAsc(R field) {
        sorts.add(SortBuilders.fieldSort(columnToString(field)).order(SortOrder.ASC));
        return self();
    }

    /**
     * 降序
     *
     * @param field 字段
     * @return Children
     */
    public Children orderByDesc(R field) {
        sorts.add(SortBuilders.fieldSort(columnToString(field)).order(SortOrder.DESC));
        return self();
    }

    /**
     * 排序
     *
     * @param field     字段
     * @param sortOrder 排序方式
     * @return Children
     */
    public Children order(R field, SortOrder sortOrder) {
        sorts.add(SortBuilders.fieldSort(columnToString(field)).order(sortOrder));
        return self();
    }


    // ========== 聚合操作 ==========

    /**
     * 分组
     *
     * @param field     字段
     * @param fieldName 分组名称
     * @return Children
     */
    public Children groupBy(R field, String fieldName) {
        aggregations.add(AggregationBuilders.terms(fieldName).field(columnToString(field)));
        return self();
    }

    /**
     * 分组
     *
     * @param field 字段
     * @return Children
     */
    public Children groupBy(R field) {
        aggregations.add(AggregationBuilders.terms(field + "Group").field(columnToString(field)));
        return self();
    }

    /**
     * 平均
     *
     * @param field 字段
     * @return Children
     */
    public Children avg(R field, String fieldName) {
        aggregations.add(AggregationBuilders.avg(fieldName).field(columnToString(field)));
        return self();
    }

    /**
     * 最大
     *
     * @param field 字段
     * @return Children
     */
    public Children max(R field, String fieldName) {
        aggregations.add(AggregationBuilders.max(fieldName).field(columnToString(field)));
        return self();
    }

    /**
     * 最小
     *
     * @param field 字段
     * @return Children
     */
    public Children min(R field, String fieldName) {
        aggregations.add(AggregationBuilders.min(fieldName).field(columnToString(field)));
        return self();
    }

    /**
     * 查询列
     *
     * @param fields 字段
     * @return Children
     */
    public Children select(R... fields) {
        this.includes = columnsToString(fields);
        return self();
    }

    /**
     * 排除列
     *
     * @param fields 字段
     * @return
     */
    public Children noSelect(R... fields) {
        this.excludes = columnsToString(fields);
        return self();
    }

    /**
     * 分页
     *
     * @param current 当前页
     * @param size    每页大小
     * @return Children
     */
    public Children page(Long current, Long size) {
        this.current = current;
        this.size = size;
        return self();
    }

    /**
     * 构建查询请求
     *
     * @return SearchSourceBuilder
     */
    public SearchRequest buildSearchRequest() {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .query(boolQuery)
//                .from((int) ((current - 1) * size))
//                .size(Math.toIntExact(size))
                // .sort(sorts)
                .fetchSource(includes, excludes);

          if(null !=current && null !=size){
              sourceBuilder.from((int) ((current - 1) * size))
                      .size(Math.toIntExact(size));
          }


        if (EmptyUtil.isNotEmpty(sorts)) {
            for (SortBuilder<?> sort : sorts) {
                sourceBuilder.sort(sort);
            }
        }

        aggregations.forEach(sourceBuilder::aggregation);
        //return sourceBuilder;
        return new SearchRequest(getIndexName()).source(sourceBuilder);
    }

    public DeleteByQueryRequest buildDeleteRequest() {
        DeleteByQueryRequest request = new DeleteByQueryRequest(getIndexName());
        request.setQuery(boolQuery);
        return request;
    }

    /**
     * 构建更新请求
     *
     * @return UpdateByQueryRequest
     */
    public UpdateByQueryRequest buildUpdateRequest() {
        UpdateByQueryRequest request = new UpdateByQueryRequest(getIndexName());
        request.setQuery(boolQuery);

        if (!updateMap.containsKey("script")) {
            // 自动生成 Painless 脚本
            StringJoiner sj = new StringJoiner("; ");
            Map<String, Object> params = new HashMap<>();
            updateMap.forEach((k, v) -> {
                sj.add("ctx._source." + k + " = params." + k);
                params.put(k, v);
            });
            request.setScript(new Script(ScriptType.INLINE, "painless", sj.toString(), params));
        } else {
            // 自定义脚本
            request.setScript(new Script((String) updateMap.get("script")));
        }

        return request;
    }

    /**
     * 获取索引名称
     *
     * @return String
     */
    public String[] getIndexName() {
        EsDocument annotation = entityClass.getAnnotation(EsDocument.class);
        String indexName = annotation.indexName();
        indexNameList.add(indexName);
        if (EmptyUtil.isEmpty(indexNameList)) {
            log.error("ES搜索:索引名称不能为空,请设置索引！");
            throw new EsException("索引名称不能为空,请设置索引！");
        }
        return indexNameList.toArray(new String[0]);
    }
}
